﻿namespace LogManager.Core.Models
{
    using LogManager.Core.Helpers;
    using LogManager.Utils.Authentification;
    using LogManager.Utils.IO;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.IO.Compression;

    public class LogFile
    {
        public string LocationRelativePath { get; private set; }
        public string ProjectionPath { get; private set; }
        public FileInfo SourceFile { get; private set; }
        public FileInfo ArchivedFile { get; private set; }
        private ILocation Location { get; set; }
        public LogFileArchivingStep LogFileStep { get; private set; }
        public Exception Error { get; private set; }
        public bool IsArchived { get { return LogFileStep == LogFileArchivingStep.SourceCleared; } }

        private object mylock = new object();

        private LogFile(FileInfo sourceFile, ILocation location)
        {
            this.LogFileStep = LogFileArchivingStep.InSource;
            this.Location = location;
            this.SourceFile = sourceFile;
            this.LocationRelativePath = SourceFile.FullName.Replace(location.FullPathSource + @"\", "");
            this.ProjectionPath = Path.Combine(location.FullPathDestination, this.LocationRelativePath + ".zip");
        }

        public void Archive(IProgress<LogFileArchivingProgress> LogFileProgress = null)
        {
            lock (mylock)
            {
                if (this.LogFileStep != LogFileArchivingStep.InSource)
                {
                    throw new InvalidOperationException("LogFile cannot be archived multiple times");
                }

                this.ReportProgress(LogFileProgress);

                try
                {
                    System.Threading.Thread.Sleep(500);
                    this.CompressToDestination();
                    this.LogFileStep = LogFileArchivingStep.Archived;
                    this.ReportProgress(LogFileProgress);

                    System.Threading.Thread.Sleep(500);
                    this.DeleteSource();
                    this.LogFileStep = LogFileArchivingStep.SourceCleared;
                    this.ReportProgress(LogFileProgress);
                }
                catch (Exception ex)
                {
                    this.Error = ex;
                    this.ReportProgress(LogFileProgress);
                }
            }
        }

        private void CompressToDestination()
        {
            using (var impDest = new Impersonator(this.Location.GetDestinationCredentials()))
            {
                (new FileInfo(this.ProjectionPath)).Directory.Create();

                using (FileStream file = new FileStream(this.ProjectionPath, FileMode.Create, FileAccess.Write))
                {
                    using (var imp = new Impersonator(this.Location.GetSourceCredentials()))
                    {
                        using (ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Create))
                        {
                            archive.CreateEntryFromFile(this.SourceFile.FullName, this.SourceFile.Name);
                        }
                    }
                }

                this.ArchivedFile = new FileInfo(this.ProjectionPath);
                this.ArchivedFile.Refresh();
            }
        }

        private void DeleteSource()
        {
            using (var imp = new Impersonator(this.Location.GetSourceCredentials()))
            {
                this.SourceFile.Delete();
                this.SourceFile.Refresh();
            }
        }

        private void ReportProgress(IProgress<LogFileArchivingProgress> LogFileProgress)
        {
            if (LogFileProgress != null)
            {
                LogFileProgress.Report(new LogFileArchivingProgress(this));
            }
        }

        public static ReadOnlyCollection<LogFile> GetFiles(ILocation location)
        {
            using (var imp = new Impersonator(location.GetSourceCredentials()))
            {
                var logFiles = new List<LogFile>();
                foreach (var fileInfo in PathHelper.GetFilesOlderThan(location.FullPathSource, location.ParentServer.RetentionDuration))
                {
                    logFiles.Add(new LogFile(fileInfo, location));
                }
                return new ReadOnlyCollection<LogFile>(logFiles);
            }
        }
    }
}
